#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
XML解析器
用于解析安卓UI的XML布局文件
"""

import xml.etree.ElementTree as ET
import re
import json
from typing import List, Dict, Optional, Tuple
from dataclasses import dataclass

@dataclass
class UIElement:
    """UI元素数据类"""
    tag: str
    text: str
    resource_id: str
    content_desc: str
    class_name: str
    bounds: str
    bounds_info: Optional[Dict]
    clickable: bool
    checkable: bool
    checked: bool
    enabled: bool
    focusable: bool
    focused: bool
    scrollable: bool
    long_clickable: bool
    password: bool
    selected: bool
    path: str
    attributes: Dict[str, str]

class XMLParser:
    def __init__(self):
        self.elements = []
        self.root_element = None
    
    def parse_xml_file(self, xml_path: str) -> List[UIElement]:
        """解析XML文件"""
        try:
            tree = ET.parse(xml_path)
            root = tree.getroot()
            self.root_element = root
            
            self.elements = []
            self._extract_elements(root)
            return self.elements
            
        except Exception as e:
            print(f"解析XML文件失败: {e}")
            return []
    
    def parse_xml_string(self, xml_string: str) -> List[UIElement]:
        """解析XML字符串"""
        try:
            root = ET.fromstring(xml_string)
            self.root_element = root
            
            self.elements = []
            self._extract_elements(root)
            return self.elements
            
        except Exception as e:
            print(f"解析XML字符串失败: {e}")
            return []
    
    def _extract_elements(self, element: ET.Element, path: str = ""):
        """递归提取元素信息"""
        current_path = f"{path}/{element.tag}" if path else element.tag
        
        # 获取元素属性
        attrs = element.attrib
        bounds = attrs.get('bounds', '')
        
        # 解析bounds
        bounds_info = self._parse_bounds(bounds)
        
        # 创建UIElement对象
        ui_element = UIElement(
            tag=element.tag,
            text=attrs.get('text', ''),
            resource_id=attrs.get('resource-id', ''),
            content_desc=attrs.get('content-desc', ''),
            class_name=attrs.get('class', ''),
            bounds=bounds,
            bounds_info=bounds_info,
            clickable=attrs.get('clickable', 'false').lower() == 'true',
            checkable=attrs.get('checkable', 'false').lower() == 'true',
            checked=attrs.get('checked', 'false').lower() == 'true',
            enabled=attrs.get('enabled', 'true').lower() == 'true',
            focusable=attrs.get('focusable', 'false').lower() == 'true',
            focused=attrs.get('focused', 'false').lower() == 'true',
            scrollable=attrs.get('scrollable', 'false').lower() == 'true',
            long_clickable=attrs.get('long-clickable', 'false').lower() == 'true',
            password=attrs.get('password', 'false').lower() == 'true',
            selected=attrs.get('selected', 'false').lower() == 'true',
            path=current_path,
            attributes=attrs.copy()
        )
        
        self.elements.append(ui_element)
        
        # 递归处理子元素
        for child in element:
            self._extract_elements(child, current_path)
    
    def _parse_bounds(self, bounds_str: str) -> Optional[Dict]:
        """解析bounds字符串"""
        if not bounds_str:
            return None
            
        # 格式: [x1,y1][x2,y2]
        match = re.match(r'\[(\d+),(\d+)\]\[(\d+),(\d+)\]', bounds_str)
        if match:
            x1, y1, x2, y2 = map(int, match.groups())
            return {
                'x1': x1, 'y1': y1, 'x2': x2, 'y2': y2,
                'width': x2 - x1,
                'height': y2 - y1,
                'center_x': (x1 + x2) // 2,
                'center_y': (y1 + y2) // 2
            }
        return None
    
    def find_element_by_text(self, text: str, exact_match: bool = True) -> List[UIElement]:
        """根据文本查找元素"""
        results = []
        for element in self.elements:
            if exact_match:
                if element.text == text:
                    results.append(element)
            else:
                if text.lower() in element.text.lower():
                    results.append(element)
        return results
    
    def find_element_by_id(self, resource_id: str) -> List[UIElement]:
        """根据资源ID查找元素"""
        results = []
        for element in self.elements:
            if element.resource_id == resource_id:
                results.append(element)
        return results
    
    def find_element_by_desc(self, content_desc: str, exact_match: bool = True) -> List[UIElement]:
        """根据内容描述查找元素"""
        results = []
        for element in self.elements:
            if exact_match:
                if element.content_desc == content_desc:
                    results.append(element)
            else:
                if content_desc.lower() in element.content_desc.lower():
                    results.append(element)
        return results
    
    def find_element_by_class(self, class_name: str) -> List[UIElement]:
        """根据类名查找元素"""
        results = []
        for element in self.elements:
            if element.class_name == class_name:
                results.append(element)
        return results
    
    def find_clickable_elements(self) -> List[UIElement]:
        """查找所有可点击的元素"""
        return [element for element in self.elements if element.clickable]
    
    def find_element_at_position(self, x: int, y: int) -> Optional[UIElement]:
        """查找指定位置的元素"""
        for element in self.elements:
            bounds = element.bounds_info
            if bounds:
                if (bounds['x1'] <= x <= bounds['x2'] and 
                    bounds['y1'] <= y <= bounds['y2']):
                    return element
        return None
    
    def find_elements_in_area(self, x1: int, y1: int, x2: int, y2: int) -> List[UIElement]:
        """查找指定区域内的所有元素"""
        results = []
        for element in self.elements:
            bounds = element.bounds_info
            if bounds:
                # 检查元素是否与指定区域有交集
                if not (bounds['x2'] < x1 or bounds['x1'] > x2 or 
                       bounds['y2'] < y1 or bounds['y1'] > y2):
                    results.append(element)
        return results
    
    def get_element_hierarchy(self) -> Dict:
        """获取元素层次结构"""
        if not self.root_element:
            return {}
        
        def build_hierarchy(element: ET.Element) -> Dict:
            result = {
                'tag': element.tag,
                'attributes': element.attrib,
                'children': []
            }
            
            for child in element:
                result['children'].append(build_hierarchy(child))
            
            return result
        
        return build_hierarchy(self.root_element)
    
    def export_to_json(self, output_path: str):
        """导出元素信息到JSON文件"""
        data = []
        for element in self.elements:
            element_dict = {
                'tag': element.tag,
                'text': element.text,
                'resource_id': element.resource_id,
                'content_desc': element.content_desc,
                'class_name': element.class_name,
                'bounds': element.bounds,
                'bounds_info': element.bounds_info,
                'clickable': element.clickable,
                'checkable': element.checkable,
                'checked': element.checked,
                'enabled': element.enabled,
                'focusable': element.focusable,
                'focused': element.focused,
                'scrollable': element.scrollable,
                'long_clickable': element.long_clickable,
                'password': element.password,
                'selected': element.selected,
                'path': element.path,
                'attributes': element.attributes
            }
            data.append(element_dict)
        
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
    
    def generate_autojs_code(self, element: UIElement) -> str:
        """为指定元素生成AutoJS代码"""
        code_lines = []
        
        # 方法1: 通过文本查找
        if element.text:
            code_lines.append(f'// 通过文本查找')
            code_lines.append(f'text("{element.text}").findOne().click();')
            code_lines.append(f'// 或者')
            code_lines.append(f'textContains("{element.text[:10]}").findOne().click();')
            code_lines.append('')
        
        # 方法2: 通过资源ID查找
        if element.resource_id:
            code_lines.append(f'// 通过资源ID查找')
            code_lines.append(f'id("{element.resource_id}").findOne().click();')
            code_lines.append('')
        
        # 方法3: 通过内容描述查找
        if element.content_desc:
            code_lines.append(f'// 通过内容描述查找')
            code_lines.append(f'desc("{element.content_desc}").findOne().click();')
            code_lines.append('')
        
        # 方法4: 通过类名查找
        if element.class_name:
            code_lines.append(f'// 通过类名查找')
            code_lines.append(f'className("{element.class_name}").findOne().click();')
            code_lines.append('')
        
        # 方法5: 通过坐标点击
        if element.bounds_info:
            bounds = element.bounds_info
            code_lines.append(f'// 通过坐标点击')
            code_lines.append(f'click({bounds["center_x"]}, {bounds["center_y"]});')
            code_lines.append('')
            code_lines.append(f'// 通过坐标范围查找')
            code_lines.append(f'bounds({bounds["x1"]}, {bounds["y1"]}, {bounds["x2"]}, {bounds["y2"]}).findOne().click();')
            code_lines.append('')
        
        # 方法6: 组合查找
        if element.text and element.class_name:
            code_lines.append(f'// 组合查找')
            code_lines.append(f'text("{element.text}").className("{element.class_name}").findOne().click();')
            code_lines.append('')
        
        # 方法7: 等待元素出现
        if element.text:
            code_lines.append(f'// 等待元素出现后点击')
            code_lines.append(f'waitFor(() => {{')
            code_lines.append(f'    return text("{element.text}").exists();')
            code_lines.append(f'}});')
            code_lines.append(f'text("{element.text}").findOne().click();')
            code_lines.append('')
        
        # 方法8: 获取元素信息
        code_lines.append(f'// 获取元素信息')
        if element.text:
            code_lines.append(f'let element = text("{element.text}").findOne();')
        elif element.resource_id:
            code_lines.append(f'let element = id("{element.resource_id}").findOne();')
        elif element.content_desc:
            code_lines.append(f'let element = desc("{element.content_desc}").findOne();')
        else:
            code_lines.append(f'let element = className("{element.class_name}").findOne();')
        
        code_lines.append(f'console.log("元素文本:", element.text());')
        code_lines.append(f'console.log("元素ID:", element.id());')
        code_lines.append(f'console.log("元素描述:", element.desc());')
        code_lines.append(f'console.log("元素类名:", element.className());')
        code_lines.append(f'console.log("元素边界:", element.bounds());')
        code_lines.append(f'console.log("是否可点击:", element.clickable());')
        code_lines.append(f'console.log("是否可选中:", element.checkable());')
        code_lines.append(f'console.log("是否已选中:", element.checked());')
        
        return '\n'.join(code_lines)
    
    def get_statistics(self) -> Dict:
        """获取元素统计信息"""
        stats = {
            'total_elements': len(self.elements),
            'clickable_elements': len([e for e in self.elements if e.clickable]),
            'text_elements': len([e for e in self.elements if e.text]),
            'id_elements': len([e for e in self.elements if e.resource_id]),
            'desc_elements': len([e for e in self.elements if e.content_desc]),
            'enabled_elements': len([e for e in self.elements if e.enabled]),
            'focusable_elements': len([e for e in self.elements if e.focusable]),
            'scrollable_elements': len([e for e in self.elements if e.scrollable]),
            'password_elements': len([e for e in self.elements if e.password]),
            'checked_elements': len([e for e in self.elements if e.checked]),
            'selected_elements': len([e for e in self.elements if e.selected])
        }
        
        # 按标签统计
        tag_stats = {}
        for element in self.elements:
            tag = element.tag
            tag_stats[tag] = tag_stats.get(tag, 0) + 1
        
        stats['tag_statistics'] = tag_stats
        
        return stats 